/***************************************************************************/
/*                                                                         */
/*  ftglyph.c                                                              */
/*                                                                         */
/*    FreeType convenience functions to handle glyphs (body).              */
/*                                                                         */
/*  Copyright 1996-2001, 2002, 2003, 2004, 2005, 2007, 2008 by             */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/

/*************************************************************************/
/*                                                                       */
/*  This file contains the definition of several convenience functions   */
/*  that can be used by client applications to easily retrieve glyph     */
/*  bitmaps and outlines from a given face.                              */
/*                                                                       */
/*  These functions should be optional if you are writing a font server  */
/*  or text layout engine on top of FreeType.  However, they are pretty  */
/*  handy for many other simple uses of the library.                     */
/*                                                                       */
/*************************************************************************/


#include <ft2build.h>
#include FT_GLYPH_H
#include FT_OUTLINE_H
#include FT_BITMAP_H
#include FT_INTERNAL_OBJECTS_H

#include "basepic.h"

/*************************************************************************/
/*                                                                       */
/* The macro FT_COMPONENT is used in trace mode.  It is an implicit      */
/* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log  */
/* messages during execution.                                            */
/*                                                                       */
#undef  FT_COMPONENT
#define FT_COMPONENT trace_glyph


/*************************************************************************/
/*************************************************************************/
/****                                                                 ****/
/****   FT_BitmapGlyph support                                        ****/
/****                                                                 ****/
/*************************************************************************/
/*************************************************************************/

FT_CALLBACK_DEF(FT_Error)
ft_bitmap_glyph_init(FT_Glyph bitmap_glyph,
                     FT_GlyphSlot slot)
{
    FT_BitmapGlyph glyph = (FT_BitmapGlyph)bitmap_glyph;
    FT_Error error = FT_Err_Ok;
    FT_Library library = FT_GLYPH(glyph)->library;


    if (slot->format != FT_GLYPH_FORMAT_BITMAP)
    {
        error = FT_Err_Invalid_Glyph_Format;
        goto Exit;
    }

    glyph->left = slot->bitmap_left;
    glyph->top = slot->bitmap_top;

    /* do lazy copying whenever possible */
    if (slot->internal->flags & FT_GLYPH_OWN_BITMAP)
    {
        glyph->bitmap = slot->bitmap;
        slot->internal->flags &= ~FT_GLYPH_OWN_BITMAP;
    }
    else
    {
        FT_Bitmap_New(&glyph->bitmap);
        error = FT_Bitmap_Copy(library, &slot->bitmap, &glyph->bitmap);
    }

Exit:
    return error;
}


FT_CALLBACK_DEF(FT_Error)
ft_bitmap_glyph_copy(FT_Glyph bitmap_source,
                     FT_Glyph bitmap_target)
{
    FT_Library library = bitmap_source->library;
    FT_BitmapGlyph source = (FT_BitmapGlyph)bitmap_source;
    FT_BitmapGlyph target = (FT_BitmapGlyph)bitmap_target;


    target->left = source->left;
    target->top = source->top;

    return FT_Bitmap_Copy(library, &source->bitmap, &target->bitmap);
}


FT_CALLBACK_DEF(void)
ft_bitmap_glyph_done(FT_Glyph bitmap_glyph)
{
    FT_BitmapGlyph glyph = (FT_BitmapGlyph)bitmap_glyph;
    FT_Library library = FT_GLYPH(glyph)->library;


    FT_Bitmap_Done(library, &glyph->bitmap);
}


FT_CALLBACK_DEF(void)
ft_bitmap_glyph_bbox(FT_Glyph bitmap_glyph,
                     FT_BBox * cbox)
{
    FT_BitmapGlyph glyph = (FT_BitmapGlyph)bitmap_glyph;


    cbox->xMin = glyph->left << 6;
    cbox->xMax = cbox->xMin + (glyph->bitmap.width << 6);
    cbox->yMax = glyph->top << 6;
    cbox->yMin = cbox->yMax - (glyph->bitmap.rows << 6);
}


FT_DEFINE_GLYPH(ft_bitmap_glyph_class,
                sizeof(FT_BitmapGlyphRec),
                FT_GLYPH_FORMAT_BITMAP,

                ft_bitmap_glyph_init,
                ft_bitmap_glyph_done,
                ft_bitmap_glyph_copy,
                0,              /* FT_Glyph_TransformFunc */
                ft_bitmap_glyph_bbox,
                0               /* FT_Glyph_PrepareFunc   */
                )


/*************************************************************************/
/*************************************************************************/
/****                                                                 ****/
/****   FT_OutlineGlyph support                                       ****/
/****                                                                 ****/
/*************************************************************************/
/*************************************************************************/


FT_CALLBACK_DEF(FT_Error)
ft_outline_glyph_init(FT_Glyph outline_glyph,
                      FT_GlyphSlot slot)
{
    FT_OutlineGlyph glyph = (FT_OutlineGlyph)outline_glyph;
    FT_Error error = FT_Err_Ok;
    FT_Library library = FT_GLYPH(glyph)->library;
    FT_Outline* source = &slot->outline;
    FT_Outline* target = &glyph->outline;


    /* check format in glyph slot */
    if (slot->format != FT_GLYPH_FORMAT_OUTLINE)
    {
        error = FT_Err_Invalid_Glyph_Format;
        goto Exit;
    }

    /* allocate new outline */
    error = FT_Outline_New(library, source->n_points, source->n_contours,
                           &glyph->outline);
    if (error)
        goto Exit;

    FT_Outline_Copy(source, target);

Exit:
    return error;
}


FT_CALLBACK_DEF(void)
ft_outline_glyph_done(FT_Glyph outline_glyph)
{
    FT_OutlineGlyph glyph = (FT_OutlineGlyph)outline_glyph;


    FT_Outline_Done(FT_GLYPH(glyph)->library, &glyph->outline);
}


FT_CALLBACK_DEF(FT_Error)
ft_outline_glyph_copy(FT_Glyph outline_source,
                      FT_Glyph outline_target)
{
    FT_OutlineGlyph source = (FT_OutlineGlyph)outline_source;
    FT_OutlineGlyph target = (FT_OutlineGlyph)outline_target;
    FT_Error error;
    FT_Library library = FT_GLYPH(source)->library;


    error = FT_Outline_New(library, source->outline.n_points,
                           source->outline.n_contours, &target->outline);
    if (!error)
        FT_Outline_Copy(&source->outline, &target->outline);

    return error;
}


FT_CALLBACK_DEF(void)
ft_outline_glyph_transform(FT_Glyph outline_glyph,
                           const FT_Matrix * matrix,
                           const FT_Vector * delta)
{
    FT_OutlineGlyph glyph = (FT_OutlineGlyph)outline_glyph;


    if (matrix)
        FT_Outline_Transform(&glyph->outline, matrix);

    if (delta)
        FT_Outline_Translate(&glyph->outline, delta->x, delta->y);
}


FT_CALLBACK_DEF(void)
ft_outline_glyph_bbox(FT_Glyph outline_glyph,
                      FT_BBox * bbox)
{
    FT_OutlineGlyph glyph = (FT_OutlineGlyph)outline_glyph;


    FT_Outline_Get_CBox(&glyph->outline, bbox);
}


FT_CALLBACK_DEF(FT_Error)
ft_outline_glyph_prepare(FT_Glyph outline_glyph,
                         FT_GlyphSlot slot)
{
    FT_OutlineGlyph glyph = (FT_OutlineGlyph)outline_glyph;


    slot->format = FT_GLYPH_FORMAT_OUTLINE;
    slot->outline = glyph->outline;
    slot->outline.flags &= ~FT_OUTLINE_OWNER;

    return FT_Err_Ok;
}


FT_DEFINE_GLYPH(ft_outline_glyph_class,
                sizeof(FT_OutlineGlyphRec),
                FT_GLYPH_FORMAT_OUTLINE,

                ft_outline_glyph_init,
                ft_outline_glyph_done,
                ft_outline_glyph_copy,
                ft_outline_glyph_transform,
                ft_outline_glyph_bbox,
                ft_outline_glyph_prepare
                )


/*************************************************************************/
/*************************************************************************/
/****                                                                 ****/
/****   FT_Glyph class and API                                        ****/
/****                                                                 ****/
/*************************************************************************/
/*************************************************************************/

static FT_Error
ft_new_glyph(FT_Library library,
             const FT_Glyph_Class* clazz,
             FT_Glyph* aglyph)
{
    FT_Memory memory = library->memory;
    FT_Error error;
    FT_Glyph glyph;


    *aglyph = 0;

    if (!FT_ALLOC(glyph, clazz->glyph_size))
    {
        glyph->library = library;
        glyph->clazz = clazz;
        glyph->format = clazz->glyph_format;

        *aglyph = glyph;
    }

    return error;
}


/* documentation is in ftglyph.h */

FT_EXPORT_DEF(FT_Error)
FT_Glyph_Copy(FT_Glyph source,
              FT_Glyph * target)
{
    FT_Glyph copy;
    FT_Error error;
    const FT_Glyph_Class* clazz;


    /* check arguments */
    if (!target)
    {
        error = FT_Err_Invalid_Argument;
        goto Exit;
    }

    *target = 0;

    if (!source || !source->clazz)
    {
        error = FT_Err_Invalid_Argument;
        goto Exit;
    }

    clazz = source->clazz;
    error = ft_new_glyph(source->library, clazz, &copy);
    if (error)
        goto Exit;

    copy->advance = source->advance;
    copy->format = source->format;

    if (clazz->glyph_copy)
        error = clazz->glyph_copy(source, copy);

    if (error)
        FT_Done_Glyph(copy);
    else
        *target = copy;

Exit:
    return error;
}


/* documentation is in ftglyph.h */

FT_EXPORT_DEF(FT_Error)
FT_Get_Glyph(FT_GlyphSlot slot,
             FT_Glyph * aglyph)
{
    FT_Library library;
    FT_Error error;
    FT_Glyph glyph;

    const FT_Glyph_Class* clazz = 0;


    if (!slot)
        return FT_Err_Invalid_Slot_Handle;

    library = slot->library;

    if (!aglyph)
        return FT_Err_Invalid_Argument;

    /* if it is a bitmap, that's easy :-) */
    if (slot->format == FT_GLYPH_FORMAT_BITMAP)
        clazz = FT_BITMAP_GLYPH_CLASS_GET;

    /* if it is an outline */
    else if (slot->format == FT_GLYPH_FORMAT_OUTLINE)
        clazz = FT_OUTLINE_GLYPH_CLASS_GET;

    else
    {
        /* try to find a renderer that supports the glyph image format */
        FT_Renderer render = FT_Lookup_Renderer(library, slot->format, 0);


        if (render)
            clazz = &render->glyph_class;
    }

    if (!clazz)
    {
        error = FT_Err_Invalid_Glyph_Format;
        goto Exit;
    }

    /* create FT_Glyph object */
    error = ft_new_glyph(library, clazz, &glyph);
    if (error)
        goto Exit;

    /* copy advance while converting it to 16.16 format */
    glyph->advance.x = slot->advance.x << 10;
    glyph->advance.y = slot->advance.y << 10;

    /* now import the image from the glyph slot */
    error = clazz->glyph_init(glyph, slot);

    /* if an error occurred, destroy the glyph */
    if (error)
        FT_Done_Glyph(glyph);
    else
        *aglyph = glyph;

Exit:
    return error;
}


/* documentation is in ftglyph.h */

FT_EXPORT_DEF(FT_Error)
FT_Glyph_Transform(FT_Glyph glyph,
                   FT_Matrix * matrix,
                   FT_Vector * delta)
{
    const FT_Glyph_Class* clazz;
    FT_Error error = FT_Err_Ok;


    if (!glyph || !glyph->clazz)
        error = FT_Err_Invalid_Argument;
    else
    {
        clazz = glyph->clazz;
        if (clazz->glyph_transform)
        {
            /* transform glyph image */
            clazz->glyph_transform(glyph, matrix, delta);

            /* transform advance vector */
            if (matrix)
                FT_Vector_Transform(&glyph->advance, matrix);
        }
        else
            error = FT_Err_Invalid_Glyph_Format;
    }
    return error;
}


/* documentation is in ftglyph.h */

FT_EXPORT_DEF(void)
FT_Glyph_Get_CBox(FT_Glyph glyph,
                  FT_UInt bbox_mode,
                  FT_BBox * acbox)
{
    const FT_Glyph_Class* clazz;


    if (!acbox)
        return;

    acbox->xMin = acbox->yMin = acbox->xMax = acbox->yMax = 0;

    if (!glyph || !glyph->clazz)
        return;
    else
    {
        clazz = glyph->clazz;
        if (!clazz->glyph_bbox)
            return;
        else
        {
            /* retrieve bbox in 26.6 coordinates */
            clazz->glyph_bbox(glyph, acbox);

            /* perform grid fitting if needed */
            if (bbox_mode == FT_GLYPH_BBOX_GRIDFIT ||
                bbox_mode == FT_GLYPH_BBOX_PIXELS)
            {
                acbox->xMin = FT_PIX_FLOOR(acbox->xMin);
                acbox->yMin = FT_PIX_FLOOR(acbox->yMin);
                acbox->xMax = FT_PIX_CEIL(acbox->xMax);
                acbox->yMax = FT_PIX_CEIL(acbox->yMax);
            }

            /* convert to integer pixels if needed */
            if (bbox_mode == FT_GLYPH_BBOX_TRUNCATE ||
                bbox_mode == FT_GLYPH_BBOX_PIXELS)
            {
                acbox->xMin >>= 6;
                acbox->yMin >>= 6;
                acbox->xMax >>= 6;
                acbox->yMax >>= 6;
            }
        }
    }
    return;
}


/* documentation is in ftglyph.h */

FT_EXPORT_DEF(FT_Error)
FT_Glyph_To_Bitmap(FT_Glyph * the_glyph,
                   FT_Render_Mode render_mode,
                   FT_Vector * origin,
                   FT_Bool destroy)
{
    FT_GlyphSlotRec dummy;
    FT_GlyphSlot_InternalRec dummy_internal;
    FT_Error error = FT_Err_Ok;
    FT_Glyph glyph;
    FT_BitmapGlyph bitmap = NULL;

    const FT_Glyph_Class* clazz;

    #ifdef FT_CONFIG_OPTION_PIC
    FT_Library library = FT_GLYPH(glyph)->library;
    #endif


    /* check argument */
    if (!the_glyph)
        goto Bad;

    /* we render the glyph into a glyph bitmap using a `dummy' glyph slot */
    /* then calling FT_Render_Glyph_Internal()                            */

    glyph = *the_glyph;
    if (!glyph)
        goto Bad;

    clazz = glyph->clazz;

    /* when called with a bitmap glyph, do nothing and return successfully */
    if (clazz == FT_BITMAP_GLYPH_CLASS_GET)
        goto Exit;

    if (!clazz || !clazz->glyph_prepare)
        goto Bad;

    FT_MEM_ZERO(&dummy, sizeof(dummy));
    FT_MEM_ZERO(&dummy_internal, sizeof(dummy_internal));
    dummy.internal = &dummy_internal;
    dummy.library = glyph->library;
    dummy.format = clazz->glyph_format;

    /* create result bitmap glyph */
    error = ft_new_glyph(glyph->library, FT_BITMAP_GLYPH_CLASS_GET,
                         (FT_Glyph*)(void*)&bitmap);
    if (error)
        goto Exit;

    #if 1
    /* if `origin' is set, translate the glyph image */
    if (origin)
        FT_Glyph_Transform(glyph, 0, origin);
    #else
    FT_UNUSED(origin);
    #endif

    /* prepare dummy slot for rendering */
    error = clazz->glyph_prepare(glyph, &dummy);
    if (!error)
        error = FT_Render_Glyph_Internal(glyph->library, &dummy, render_mode);

    #if 1
    if (!destroy && origin)
    {
        FT_Vector v;


        v.x = -origin->x;
        v.y = -origin->y;
        FT_Glyph_Transform(glyph, 0, &v);
    }
    #endif

    if (error)
        goto Exit;

    /* in case of success, copy the bitmap to the glyph bitmap */
    error = ft_bitmap_glyph_init((FT_Glyph)bitmap, &dummy);
    if (error)
        goto Exit;

    /* copy advance */
    bitmap->root.advance = glyph->advance;

    if (destroy)
        FT_Done_Glyph(glyph);

    *the_glyph = FT_GLYPH(bitmap);

Exit:
    if (error && bitmap)
        FT_Done_Glyph(FT_GLYPH(bitmap));

    return error;

Bad:
    error = FT_Err_Invalid_Argument;
    goto Exit;
}


/* documentation is in ftglyph.h */

FT_EXPORT_DEF(void)
FT_Done_Glyph(FT_Glyph glyph)
{
    if (glyph)
    {
        FT_Memory memory = glyph->library->memory;
        const FT_Glyph_Class* clazz = glyph->clazz;


        if (clazz->glyph_done)
            clazz->glyph_done(glyph);

        FT_FREE(glyph);
    }
}


/* END */